import type { ReactElement } from 'react';
import {
    Alert,
    Checkbox,
    Form,
    Grid,
    GridItem,
    List,
    ListItem,
    PageSection,
    TextInput,
    ToggleGroup,
    ToggleGroupItem,
} from '@patternfly/react-core';
import cloneDeep from 'lodash/cloneDeep';
import * as yup from 'yup';

import type { QuayImageIntegration } from 'types/imageIntegration.proto';

import FormMessage from 'Components/PatternFly/FormMessage';
import FormTestButton from 'Components/PatternFly/FormTestButton';
import FormSaveButton from 'Components/PatternFly/FormSaveButton';
import FormCancelButton from 'Components/PatternFly/FormCancelButton';

import usePageState from '../../hooks/usePageState';
import useIntegrationForm from '../useIntegrationForm';
import type { IntegrationFormProps } from '../integrationFormTypes';

import IntegrationFormActions from '../IntegrationFormActions';
import FormLabelGroup from '../FormLabelGroup';

import { categoriesUtilsForRegistryScanner } from '../../utils/integrationUtils';

const { categoriesAlternatives, getCategoriesText, matchCategoriesAlternative, validCategories } =
    categoriesUtilsForRegistryScanner;

export type QuayIntegrationFormValues = {
    config: QuayImageIntegration;
    updatePassword: boolean;
};

export const validationSchema = yup.object().shape({
    config: yup.object().shape({
        name: yup.string().trim().required('An integration name is required'),
        categories: yup
            .array()
            .of(yup.string().trim().oneOf(validCategories))
            .min(1, 'Must have at least one type selected')
            .required('A category is required'),
        quay: yup.object().shape({
            endpoint: yup.string().trim().required('An endpoint is required'),
            insecure: yup.bool(),
        }),
        skipTestIntegration: yup.bool(),
        type: yup.string().matches(/quay/),
    }),
    updatePassword: yup.bool(),
});

export const defaultValues: QuayIntegrationFormValues = {
    config: {
        id: '',
        name: '',
        categories: ['REGISTRY'],
        quay: {
            endpoint: '',
            oauthToken: '',
            insecure: false,
            registryRobotCredentials: null,
        },
        autogenerated: false,
        clusterId: '',
        skipTestIntegration: false,
        type: 'quay',
    },
    updatePassword: true,
};

// Given initial values for an existing integration, return form initial values.
function computeInitialValues(
    initialValues: QuayImageIntegration | null
): QuayIntegrationFormValues {
    if (initialValues) {
        /*
         * Clear token or password because backend returns '******'
         * to represent that there are currently stored credentials.
         *
         * However, this convention prevents the rare edge case
         * to remove stored credentials from an existing integration.
         */
        const config = cloneDeep(initialValues);
        config.quay.oauthToken = '';
        if (config.quay.registryRobotCredentials?.password) {
            config.quay.registryRobotCredentials.password = '';
        }

        // Don't assume user wants to change password; that has caused confusing UX.
        const updatePassword = false;
        /*
         * DEPRECATED: this is no longer needed
         *
         * Special case for Quay integration,
         * because unauthenticated is an implicit instead of explicit property.
         * If an existing integration does not have stored credentials,
         * updatePassword is initially cleared, so user must tick it to add stored credentials.
         *
         * However, assignment statement is from positive instead of negative viewpoint.
         */
        /*
        const hasInitialOauthToken = Boolean(initialValues.quay.oauthToken);
        const hasInitialRobotAccount = Boolean(initialValues.quay.registryRobotCredentials);
        const updatePassword = hasInitialOauthToken || hasInitialRobotAccount;
        */

        // Edit or view existing integration.
        return { config, updatePassword };
    }

    // Create new integration.
    return cloneDeep(defaultValues);
}

function QuayIntegrationForm({
    initialValues = null,
    isEditable = false,
}: IntegrationFormProps<QuayImageIntegration>): ReactElement {
    // Refer to stored token in placeholder only if it exists initially.
    const hasInitialOauthToken = Boolean(initialValues?.quay?.oauthToken);

    const {
        values,
        touched,
        errors,
        dirty,
        isValid,
        setFieldValue,
        handleBlur,
        isSubmitting,
        isTesting,
        onSave,
        onTest,
        onCancel,
        message,
    } = useIntegrationForm<QuayIntegrationFormValues>({
        initialValues: computeInitialValues(initialValues),
        validationSchema,
    });
    const { isCreating } = usePageState();

    function onChange(value, event) {
        return setFieldValue(event.target.id, value);
    }

    function onChangeRobotUsername(value, event) {
        if (value || values.config.quay.registryRobotCredentials?.password) {
            return setFieldValue(event.target.id, value);
        }

        // Replace with null because both empty fails backend validation.
        return setFieldValue('config.quay.registryRobotCredentials', null);
    }

    function onChangeRobotPassword(value, event) {
        if (value || values.config.quay.registryRobotCredentials?.username) {
            return setFieldValue(event.target.id, value);
        }

        // Replace with null because both empty fails backend validation.
        return setFieldValue('config.quay.registryRobotCredentials', null);
    }

    // Clear form values for stored credentials whether click to select or clear the checkbox.
    function onUpdateCredentialsChange(value, event) {
        setFieldValue('config.quay.oauthToken', '');
        if (values.config.quay.registryRobotCredentials) {
            if (values.config.quay.registryRobotCredentials.username) {
                setFieldValue('config.quay.registryRobotCredentials.password', '');
            } else {
                setFieldValue('config.quay.registryRobotCredentials', null);
            }
        }
        return setFieldValue(event.target.id, value);
    }

    return (
        <>
            <PageSection variant="light" isFilled hasOverflowScroll>
                <FormMessage message={message} />
                <Form isWidthLimited>
                    <FormLabelGroup
                        label="Integration name"
                        isRequired
                        fieldId="config.name"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            isRequired
                            type="text"
                            id="config.name"
                            placeholder="(ex. Quay)"
                            value={values.config.name}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                    <FormLabelGroup
                        label="Type"
                        isRequired
                        fieldId="config.categories"
                        touched={touched}
                        errors={errors}
                    >
                        <ToggleGroup id="config.categories" areAllGroupsDisabled={!isEditable}>
                            {categoriesAlternatives.map((categoriesAlternative) => {
                                const [categoriesAlternativeItem0] = categoriesAlternative;
                                const text = getCategoriesText(categoriesAlternativeItem0);
                                const isSelected = matchCategoriesAlternative(
                                    categoriesAlternative,
                                    values.config.categories
                                );
                                return (
                                    <ToggleGroupItem
                                        key={text}
                                        text={text}
                                        isSelected={isSelected}
                                        onChange={() =>
                                            setFieldValue(
                                                'config.categories',
                                                categoriesAlternativeItem0
                                            )
                                        }
                                    />
                                );
                            })}
                        </ToggleGroup>
                    </FormLabelGroup>
                    <FormLabelGroup
                        label="Endpoint"
                        isRequired
                        fieldId="config.quay.endpoint"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            isRequired
                            type="text"
                            id="config.quay.endpoint"
                            placeholder="(ex. quay.io)"
                            value={values.config.quay.endpoint}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                    {isEditable && (
                        <Alert variant="info" isInline title="Authentication" component="p">
                            <List>
                                {values.config.categories.includes('SCANNER') ? (
                                    <ListItem>
                                        Private repositories: <strong>OAuth token</strong> is{' '}
                                        <strong>required</strong> to scan images.
                                    </ListItem>
                                ) : (
                                    <ListItem>
                                        Private repositories: <strong>OAuth token</strong> is{' '}
                                        <strong>deprecated</strong> for any access to images (except
                                        for scanning).
                                    </ListItem>
                                )}
                                {values.config.categories.includes('REGISTRY') && (
                                    <ListItem>
                                        Private repositories: <strong>Robot account</strong> is{' '}
                                        <strong>recommended</strong> for any access to images{' '}
                                        (except for scanning).
                                    </ListItem>
                                )}
                                {values.config.categories.includes('REGISTRY') &&
                                    values.config.categories.includes('SCANNER') && (
                                        <ListItem>
                                            Private repositories: You can omit{' '}
                                            <strong>Robot username</strong> and{' '}
                                            <strong>Robot password</strong> to use{' '}
                                            <strong>OAuth token</strong> for all access to images.
                                        </ListItem>
                                    )}
                                <ListItem>
                                    Public repositories: Omit authentication for access to images.
                                </ListItem>
                            </List>
                        </Alert>
                    )}
                    {!isCreating && isEditable && (
                        <FormLabelGroup
                            fieldId="updatePassword"
                            helperText="Enable this option to replace currently stored credentials (if any)"
                            errors={errors}
                        >
                            <Checkbox
                                label="Update stored credentials"
                                id="updatePassword"
                                isChecked={values.updatePassword}
                                onChange={(event, value) => onUpdateCredentialsChange(value, event)}
                                onBlur={handleBlur}
                                isDisabled={!isEditable}
                            />
                        </FormLabelGroup>
                    )}
                    <FormLabelGroup
                        label="OAuth token"
                        fieldId="config.quay.oauthToken"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            type="text"
                            id="config.quay.oauthToken"
                            value={values.config.quay.oauthToken}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable || !values.updatePassword}
                            placeholder={
                                values.updatePassword || !hasInitialOauthToken
                                    ? ''
                                    : 'Currently-stored token will be used.'
                            }
                        />
                    </FormLabelGroup>
                    {values.config.categories.includes('REGISTRY') && (
                        <Grid hasGutter>
                            <GridItem span={12}>
                                <Alert
                                    variant="info"
                                    isInline
                                    title="Use your Quay.io or Quay robot account username and password"
                                    component="p"
                                />
                            </GridItem>
                            <GridItem span={12} lg={6}>
                                <FormLabelGroup
                                    label="Username"
                                    fieldId="config.quay.registryRobotCredentials.username"
                                    touched={touched}
                                    errors={errors}
                                >
                                    <TextInput
                                        type="text"
                                        id="config.quay.registryRobotCredentials.username"
                                        value={
                                            values.config.quay.registryRobotCredentials?.username ??
                                            ''
                                        }
                                        onChange={(event, value) =>
                                            onChangeRobotUsername(value, event)
                                        }
                                        onBlur={handleBlur}
                                        isDisabled={!isEditable || !values.updatePassword}
                                    />
                                </FormLabelGroup>
                            </GridItem>
                            <GridItem span={12} lg={6}>
                                <FormLabelGroup
                                    label="Password"
                                    fieldId="config.quay.registryRobotCredentials.password"
                                    touched={touched}
                                    errors={errors}
                                >
                                    <TextInput
                                        type="password"
                                        id="config.quay.registryRobotCredentials.password"
                                        value={
                                            values.config.quay.registryRobotCredentials?.password ??
                                            ''
                                        }
                                        onChange={(event, value) =>
                                            onChangeRobotPassword(value, event)
                                        }
                                        onBlur={handleBlur}
                                        isDisabled={!isEditable || !values.updatePassword}
                                        placeholder={
                                            values.updatePassword ||
                                            !values.config.quay.registryRobotCredentials?.username
                                                ? ''
                                                : 'Currently-stored password will be used.'
                                        }
                                    />
                                </FormLabelGroup>
                            </GridItem>
                        </Grid>
                    )}
                    <FormLabelGroup
                        fieldId="config.quay.insecure"
                        touched={touched}
                        errors={errors}
                    >
                        <Checkbox
                            label="Disable TLS certificate validation (insecure)"
                            id="config.quay.insecure"
                            aria-label="disable tls certificate validation"
                            isChecked={values.config.quay.insecure}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                    <FormLabelGroup
                        fieldId="config.skipTestIntegration"
                        touched={touched}
                        errors={errors}
                    >
                        <Checkbox
                            label="Create integration without testing"
                            id="config.skipTestIntegration"
                            aria-label="skip test integration"
                            isChecked={values.config.skipTestIntegration}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                </Form>
            </PageSection>
            {isEditable && (
                <IntegrationFormActions>
                    <FormSaveButton
                        onSave={onSave}
                        isSubmitting={isSubmitting}
                        isTesting={isTesting}
                        isDisabled={!dirty || !isValid}
                    >
                        Save
                    </FormSaveButton>
                    <FormTestButton
                        onTest={onTest}
                        isSubmitting={isSubmitting}
                        isTesting={isTesting}
                        isDisabled={!isValid}
                    >
                        Test
                    </FormTestButton>
                    <FormCancelButton onCancel={onCancel}>Cancel</FormCancelButton>
                </IntegrationFormActions>
            )}
        </>
    );
}

export default QuayIntegrationForm;
